﻿using SharpCompress.Readers;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;

namespace ffmpegSet
{
    internal class Program
    {
        private static string getffmpeg()
        {
            var pp = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
            var ps = pp.Split(';');
            foreach (var item in ps)
            {
                var file = Path.Combine(item, "ffmpeg.exe");
                if (File.Exists(file)) return file;
            }
            return null;
        }

        private static void Main(string[] args)
        {
            Check();
            string archive = "";
            if (Environment.Is64BitOperatingSystem)
            {
                archive = "ffmpegData/ffmpeg-latest-win64-static.zip";
            }
            else
            {
                archive = "ffmpegData/ffmpeg-latest-win32-static.zip";
            }
            var ffmpegpath = getffmpeg();

            try
            {
                Updata(ffmpegpath, archive);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            Console.WriteLine("finish");

            Console.Read();
        }

        private static void Updata(string ffmpegpath, string archive)
        {
            if (ffmpegpath == null)
            {
                var ffmpegdir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "ffmpeg");
                Directory.CreateDirectory(ffmpegdir);
                using (Stream stream = File.OpenRead(archive))
                {
                    var reader = ReaderFactory.Open(stream);
                    while (reader.MoveToNextEntry())
                    {
                        if (reader.Entry.Key.EndsWith(".exe", StringComparison.CurrentCultureIgnoreCase))
                        {
                            Console.WriteLine(reader.Entry.Key);
                            reader.WriteEntryToDirectory(ffmpegdir, new ExtractionOptions()
                            {
                                ExtractFullPath = false,
                                Overwrite = true,
                                PreserveFileTime = true
                            });
                        }
                    }
                }
                var p = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
                p = p.EndsWith(";") ? p + ffmpegdir : p + ";" + ffmpegdir;
                Environment.SetEnvironmentVariable("PATH", p, EnvironmentVariableTarget.Machine);
                Console.WriteLine("SetEnvironmentVariable  Ok!");
            }
            else
            {
                var info = new FileInfo(ffmpegpath);
                var ffmpegdir = info.DirectoryName;

                using (Stream stream = File.OpenRead(archive))
                {
                    var reader = ReaderFactory.Open(stream);
                    while (reader.MoveToNextEntry())
                    {
                        if (reader.Entry.Key.StartsWith("ffmpeg-latest-win64-static/bin/", StringComparison.CurrentCultureIgnoreCase) && !reader.Entry.IsDirectory)
                        {
                            var inf = new FileInfo(Path.Combine(ffmpegdir, Path.GetFileName(reader.Entry.Key)));

                            if (inf.LastWriteTime < reader.Entry.LastModifiedTime || !inf.Exists)
                            {
                                Console.WriteLine(reader.Entry.Key);
                                reader.WriteEntryToDirectory(ffmpegdir, new ExtractionOptions()
                                {
                                    ExtractFullPath = false,
                                    Overwrite = true,
                                    PreserveFileTime = true
                                });
                                Console.WriteLine("Updated!");
                            }
                        }
                    }
                }
            }
        }

        private static void Check()
        {
            var url = "https://ffmpeg.zeranoe.com/builds";
            var dir = "ffmpegData";
            var x86 = "ffmpeg-latest-win32-static.zip";
            var x64 = "ffmpeg-latest-win64-static.zip";

            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            if (!File.Exists($"{dir}/{x86}"))
            {
                Console.WriteLine($"create {dir}/{x86}");
                Console.WriteLine(HttpDldFile.Download($"{url}/win32/static/{x86}", $"{dir}/{x86}"));
            }
            else
            {
                long len = HttpDldFile.GetHttpLength($"{url}/win32/static/{x86}");
                if (len > 0 && (int)len != File.ReadAllBytes($"{dir}/{x86}").Length)
                {
                    File.Delete($"{dir}/{x86}");
                    Console.WriteLine($"create {dir}/{x86}");
                    Console.WriteLine(HttpDldFile.Download($"{url}/win32/static/{x86}", $"{dir}/{x86}"));
                }
            }
            if (!File.Exists($"{dir}/{x64}"))
            {
                Console.WriteLine($"create {dir}/{x64}");
                Console.WriteLine(HttpDldFile.Download($"{url}/win64/static/{x64}", $"{dir}/{x64}"));
            }
            else
            {
                long len = HttpDldFile.GetHttpLength($"{url}/win64/static/{x64}");
                if (len > 0 && (int)len != File.ReadAllBytes($"{dir}/{x64}").Length)
                {
                    File.Delete($"{dir}/{x64}");
                    Console.WriteLine($"create {dir}/{x64}");
                    Console.WriteLine(HttpDldFile.Download($"{url}/win64/static/{x64}", $"{dir}/{x64}"));
                }
            }
        }
    }

    internal class HttpDldFile
    {
        /// <summary>
        /// Http方式下载文件
        /// </summary>
        /// <param name="url">http地址</param>
        /// <param name="localfile">本地文件</param>
        /// <returns></returns>
        public static bool Download(string url, string localfile)
        {
            bool flag = false;
            long startPosition = 0; // 上次下载的文件起始位置
            FileStream writeStream; // 写入本地文件流对象

            long remoteFileLength = GetHttpLength(url);// 取得远程文件长度
            System.Console.WriteLine("remoteFileLength=" + remoteFileLength);
            if (remoteFileLength == 745)
            {
                System.Console.WriteLine("远程文件不存在.");
                return false;
            }

            // 判断要下载的文件夹是否存在
            if (File.Exists(localfile))
            {
                writeStream = File.OpenWrite(localfile);             // 存在则打开要下载的文件
                startPosition = writeStream.Length;                  // 获取已经下载的长度

                if (startPosition >= remoteFileLength)
                {
                    System.Console.WriteLine("本地文件长度" + startPosition + "已经大于等于远程文件长度" + remoteFileLength);
                    writeStream.Close();

                    return false;
                }
                else
                {
                    writeStream.Seek(startPosition, SeekOrigin.Current); // 本地文件写入位置定位
                }
            }
            else
            {
                writeStream = new FileStream(localfile, FileMode.Create);// 文件不保存创建一个文件
                startPosition = 0;
            }

            try
            {
                HttpWebRequest myRequest = (HttpWebRequest)HttpWebRequest.Create(url);// 打开网络连接

                if (startPosition > 0)
                {
                    myRequest.AddRange((int)startPosition);// 设置Range值,与上面的writeStream.Seek用意相同,是为了定义远程文件读取位置
                }

                Stream readStream = myRequest.GetResponse().GetResponseStream();// 向服务器请求,获得服务器的回应数据流

                byte[] btArray = new byte[512];// 定义一个字节数据,用来向readStream读取内容和向writeStream写入内容
                int contentSize = readStream.Read(btArray, 0, btArray.Length);// 向远程文件读第一次

                long currPostion = startPosition;
                ProgressBar progressBar = new ProgressBar(Console.CursorLeft, Console.CursorTop, 50, ProgressBarType.Character);
                while (contentSize > 0)// 如果读取长度大于零则继续读
                {
                    currPostion += contentSize;
                    int percent = (int)(currPostion * 100 / remoteFileLength);
                    progressBar.Dispaly(percent);
                    //System.Console.WriteLine("percent=" + percent + "%");

                    writeStream.Write(btArray, 0, contentSize);// 写入本地文件
                    contentSize = readStream.Read(btArray, 0, btArray.Length);// 继续向远程文件读取
                }
                System.Console.WriteLine();
                //关闭流
                writeStream.Close();
                readStream.Close();

                flag = true;        //返回true下载成功
            }
            catch (Exception)
            {
                writeStream.Close();
                flag = false;       //返回false下载失败
            }

            return flag;
        }

        // 从文件头得到远程文件的长度
        public static long GetHttpLength(string url)
        {
            long length = 0;

            try
            {
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);// 打开网络连接
                HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();

                if (rsp.StatusCode == HttpStatusCode.OK)
                {
                    length = rsp.ContentLength;// 从文件头得到远程文件的长度
                }

                rsp.Close();
                return length;
            }
            catch (Exception)
            {
                return length;
            }
        }
    }

    /// <summary>
    /// 进度条类型
    /// </summary>
    public enum ProgressBarType
    {
        /// <summary>
        /// 字符
        /// </summary>
        Character,

        /// <summary>
        /// 彩色
        /// </summary>
        Multicolor
    }

    public class ProgressBar
    {
        /// <summary>
        /// 光标的列位置。将从 0 开始从左到右对列进行编号。
        /// </summary>
        public int Left { get; set; }

        /// <summary>
        /// 光标的行位置。从上到下，从 0 开始为行编号。
        /// </summary>
        public int Top { get; set; }

        /// <summary>
        /// 进度条宽度。
        /// </summary>
        public int Width { get; set; }

        /// <summary>
        /// 进度条当前值。
        /// </summary>
        public int Value { get; set; }

        /// <summary>
        /// 进度条类型
        /// </summary>
        public ProgressBarType ProgressBarType { get; set; }

        private ConsoleColor colorBack;
        private ConsoleColor colorFore;

        public ProgressBar() : this(Console.CursorLeft, Console.CursorTop)
        {
        }

        public ProgressBar(int left, int top, int width = 50, ProgressBarType ProgressBarType = ProgressBarType.Multicolor)
        {
            this.Left = left;
            this.Top = top;
            this.Width = width;
            this.ProgressBarType = ProgressBarType;

            // 清空显示区域；
            Console.SetCursorPosition(Left, Top);
            for (int i = left; ++i < Console.WindowWidth;) { Console.Write(" "); }

            if (this.ProgressBarType == ProgressBarType.Multicolor)
            {
                // 绘制进度条背景；
                colorBack = Console.BackgroundColor;
                Console.SetCursorPosition(Left, Top);
                Console.BackgroundColor = ConsoleColor.DarkCyan;
                for (int i = 0; ++i <= width;) { Console.Write(" "); }
                Console.BackgroundColor = colorBack;
            }
            else
            {
                // 绘制进度条背景；
                Console.SetCursorPosition(left, top);
                Console.Write("[");
                Console.SetCursorPosition(left + width - 1, top);
                Console.Write("]");
            }
        }

        public int Dispaly(int value)
        {
            return Dispaly(value, null);
        }

        public int Dispaly(int value, string msg)
        {
            if (this.Value != value)
            {
                this.Value = value;

                if (this.ProgressBarType == ProgressBarType.Multicolor)
                {
                    // 保存背景色与前景色；
                    colorBack = Console.BackgroundColor;
                    colorFore = Console.ForegroundColor;
                    // 绘制进度条进度
                    Console.BackgroundColor = ConsoleColor.Yellow;
                    Console.SetCursorPosition(this.Left, this.Top);
                    Console.Write(new string(' ', (int)Math.Round(this.Value / (100.0 / this.Width))));
                    Console.BackgroundColor = colorBack;

                    // 更新进度百分比,原理同上.
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.SetCursorPosition(this.Left + this.Width + 1, this.Top);
                    if (string.IsNullOrWhiteSpace(msg)) { Console.Write("{0}%", this.Value); } else { Console.Write(msg); }
                    Console.ForegroundColor = colorFore;
                }
                else
                {
                    // 绘制进度条进度
                    Console.SetCursorPosition(this.Left + 1, this.Top);
                    Console.Write(new string('*', (int)Math.Round(this.Value / (100.0 / (this.Width - 2)))));
                    // 显示百分比
                    Console.SetCursorPosition(this.Left + this.Width + 1, this.Top);
                    if (string.IsNullOrWhiteSpace(msg)) { Console.Write("{0}%", this.Value); } else { Console.Write(msg); }
                }
            }
            return value;
        }
    }

    /// <summary>
    /// Cmd 的摘要说明。
    /// </summary>
    public class Cmd
    {
        private Process proc = null;

        /// <summary>
        /// 构造方法
        /// </summary>
        public Cmd()
        {
            proc = new Process();
        }

        /// <summary>
        /// 执行CMD语句
        /// </summary>
        /// <param name="cmd">要执行的CMD命令</param>
        public string RunCmd(string cmd)
        {
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.FileName = "cmd.exe";
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.Start();
            proc.StandardInput.WriteLine(cmd);
            System.Threading.Thread.Sleep(2000);
            proc.StandardInput.WriteLine("exit");
            string outStr = proc.StandardOutput.ReadToEnd();
            proc.Close();
            return outStr;
        }

        /// <summary>
        /// 打开软件并执行命令
        /// </summary>
        /// <param name="programName">软件路径加名称（.exe文件）</param>
        /// <param name="cmd">要执行的命令</param>
        public void RunProgram(string programName, string cmd)
        {
            Process proc = new Process();
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.FileName = programName;
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.Start();
            if (cmd.Length != 0)
            {
                proc.StandardInput.WriteLine(cmd);
            }
            proc.Close();
        }

        /// <summary>
        /// 打开软件
        /// </summary>
        /// <param name="programName">软件路径加名称（.exe文件）</param>
        public void RunProgram(string programName)
        {
            this.RunProgram(programName, "");
        }
    }
}